package com.starblink.frame.base.adapter

import android.util.ArrayMap
import android.util.SparseArray
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView

open class MutableAdapter(list: List<Any>) : BaseAdapter<BaseVH<*>>(list = list) {

    private val itemTypeToViewHolderMap = SparseArray<ViewHolderVm<*>>()
    private val javaClassToItemType = hashMapOf<Class<*>, Int>()
    private val itemTypeOffsets = ArrayMap<Class<*>, ItemTypeOffset<*>>()

    private var itemTypeCounter = 1

    /** onBindViewHolder触发监听器集合 */
    var onBindViewHolders = mutableListOf<OnMutableHolderListener>()

    /** 当前Adapter被setAdapter才不为null */
    var rv: RecyclerView? = null

    /**
     * 添加新类型, 或覆盖原有类型
     */
    fun <T> addVhDelegate(javaClass: Class<*>, vh: CreateVh<T>) {
        val itemType = javaClassToItemType[javaClass]
        if (itemType == null) {
            javaClassToItemType[javaClass] = itemTypeCounter
            itemTypeToViewHolderMap.put(itemTypeCounter, ViewHolderVm(vh))
            itemTypeCounter++
        } else {
            // 原本是 multi-type 的 item, 则移除 offset
            // 但该 item 的其他 type 不会被覆盖, 仍然保存在 itemTypeToViewHolderMap 中
            itemTypeOffsets.remove(javaClass)
            itemTypeToViewHolderMap.put(itemType, ViewHolderVm(vh))
        }
    }

    fun getItemType(javaClass: Class<*>): Int = javaClassToItemType[javaClass] ?: -1

    inline fun <reified T> addVhDelegate(noinline vh: CreateVh<T>) {
        addVhDelegate(T::class.java, vh)
    }

    fun <T> addAlternativeVhDelegates(
        clz: Class<*>,
        offset: ItemTypeOffset<T>,
        vararg vh: CreateVh<T>
    ) {
        if (javaClassToItemType.containsKey(clz) && itemTypeOffsets.containsKey(clz)) {
            return
        }
        itemTypeOffsets[clz] = offset
        javaClassToItemType[clz] = itemTypeCounter
        vh.forEach {
            itemTypeToViewHolderMap.put(itemTypeCounter++, ViewHolderVm(it))
        }
    }

    /**
     * @param offset 当存在 multi-type item 时, 对应的 view type 的偏移 (从 0 开始)
     */
    fun <T> addVhDelegates(clz: Class<*>, offset: ItemTypeOffset<T>, vararg vh: CreateVh<T>) {
        if (javaClassToItemType.containsKey(clz) && itemTypeOffsets.containsKey(clz)) {
            return
        }
        itemTypeOffsets[clz] = offset
        javaClassToItemType[clz] = itemTypeCounter
        vh.forEach {
            itemTypeToViewHolderMap.put(itemTypeCounter++, ViewHolderVm(it))
        }
    }

    inline fun <reified T> addVhDelegates(
        vararg vh: CreateVh<T>,
        noinline offset: ItemTypeOffset<T>
    ) {
        addVhDelegates(T::class.java, offset, *vh)
    }

    override fun getItemViewType(position: Int): Int {
        val item = list[position]
        val clz = item.javaClass

        val offset = itemTypeOffsets[clz]
        // 如果存在多个 data 同类型的 item
        // 需要通过 offset 计算 delegate 的偏移
        val offsetValue = if (offset != null) {
            offset as ItemTypeOffset<Any>
            offset(item, position)
        } else {
            0
        }

        return (javaClassToItemType[clz] ?: 0) + offsetValue
    }

    override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
        super.onAttachedToRecyclerView(recyclerView)
        this.rv = recyclerView
    }

    override fun onCreateVH(
        parent: ViewGroup,
        viewType: Int,
        flag: Int,
    ): BaseVH<out Any?> =
        itemTypeToViewHolderMap.get(viewType)?.getVh(parent) ?: EmptyVH(parent)

    override fun onBindViewHolder(holder: BaseVH<*>, position: Int) {
        (holder as? BaseVH<Any>)?.onBind((list[position]), position)
        onBindViewHolders.forEach { it.onBindViewHolder(rv!!, this, position) }
    }


    override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
        super.onDetachedFromRecyclerView(recyclerView)
        itemTypeToViewHolderMap.clear()
        javaClassToItemType.clear()
        itemTypeCounter = 1
    }
}


typealias CreateVh<T> = (parent: ViewGroup) -> BaseVH<T>

/**
 * 根据 item 与 item view 的位置, 返回 offset (从 0 开始)
 */
internal typealias ItemTypeOffset<T> = (item: T, pos: Int) -> Int

internal class ViewHolderVm<T>(
    private val vh: CreateVh<T>
) {
    fun getVh(parent: ViewGroup): BaseVH<T> = vh.invoke(parent)
}